package com.ingenic.glass.camera.pqylive;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Executors;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.json.JSONException;
import org.json.JSONObject;

import cn.ingenic.glasssync.services.SyncData;
import cn.ingenic.glasssync.services.SyncException;

import com.ingenic.glass.camera.live.CameraRTMPLive;
import com.ingenic.glass.camera.pqylive.PqyLiveModule;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;

public class PqyLiveBroadcastReceiver extends BroadcastReceiver {

        private static String TAG = "PqyLiveBroadcastReceiver";

        private static final String TASKRET_SUCCESS = "success";
        private static final String TASKRET_FAILED = "failed";

        private static final String KEY_LIVEUID = "live.uid.pqy";

        private static String mUserId = null;
        private static String mSessionId = null;
        private static String mRoomId = null;
        private  static char [] randCharAry = new char[62];
        private static String mCurrentLiveStatus = "stop_live";

        static {
                // Initialized random character array.
                for (int i = 0; i < 10; i++) {
                        randCharAry[i] = (char) (i + 48);
                }
                for (int i = 10, j = 65; i < 36; i++, j++) {
                        randCharAry[i] = (char) j;
                }
                for (int i = 36, j = 97; i < 62; i++, j++) {
                        randCharAry[i] = (char) j;
                }
        }

        public void live (Context context) {
                if (null == mUserId) { // Start live
                        login(context, true);
                } else { // Stop live
                        room(context, false, true);
                }
        }

        @Override
        public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                Log.d(TAG, action);
                if ("com.ingenic.glass.camera.glasspqylive.LIVE".equals(action)) {
                        if (null == mUserId) { // Start live
                                login(context, true);
                        } else { // Stop live
                                room(context, false, true);
                        }
                } else if ("com.ingenic.glass.camera.glasspqylive.LOGIN".equals(action)) {
                        login(context, false);
                } else if ("com.ingenic.glass.camera.glasspqylive.CREATE".equals(action)) {
                        room(context, true, false);
                } else if ("com.ingenic.glass.camera.glasspqylive.CLOSE".equals(action)) {
                        room(context, false, false);
                } else if ("com.ingenic.glass.camera.glasspqylive.LOGOUT".equals(action)) {
                        logout();
                } else if (CameraRTMPLive.CAMERA_ACTION_STATUS.equals(action)) {
                        String status = intent.getStringExtra("status");
                        Log.d(TAG, "RTMP live status: " + status);
                        try {
                                setCurrentLiveStatus(status);
                                SyncData data = new SyncData();
                                data.putInt("type", PqyLiveModule.TYPE_LIVESTATUS);
                                data.putString(PqyLiveModule.KEY_LIVESTATUS, status);
                                PqyLiveModule.getInstance(context).send(data);
                        } catch (SyncException e) {
                                e.printStackTrace();
                        }
                }
        }

        private void login(Context context, boolean createRoom) {
                try {
                        String params = genLoginUrlParam();
                        if (TextUtils.isEmpty(params)) {
                                Log.e(TAG, "Generate login url parameters failed.");
                                return ;
                        }
                        LoginTask task = new LoginTask(context, createRoom);
                        task.executeOnExecutor(Executors.newCachedThreadPool()
                                        , "https://api.penquanyun.com/auth/mobile/login?" + params);
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }

        private void room(Context context, boolean isCreateRoom, boolean logout) {
                if (null == mUserId) {
                        return ;
                }
                String urlParams = null;
                String url = null;
                if (isCreateRoom) { // Create Room
                        urlParams = "c=room&a=createfromdev&from=1&ping_avg=0&dev=glass";
                        url = "http://test.biz.live.penquanyun.com/caller?" + urlParams;
                } else { // Close Room
                        if (null == mRoomId) {
                                return ;
                        }
                        // Stop live
                        Intent intent = new Intent();
                        intent.setAction("com.ingenic.glass.camera.CameraRTMPLive");
                        context.sendBroadcast(intent);
                        urlParams = "c=room&a=close&dev=glass&roomid=" + mRoomId;
                        url = "http://test.biz.live.penquanyun.com/caller?" + urlParams;
                }
                Log.d(TAG, "url = " + url);
                String cookies = null;
                try {
                        cookies = "fcappid=live_1; appcode=100; coopid=777; appver=1.0.0; "
                                        + "appid=777; osver=4.3; channel=Test; os=android; "
                                        + "model=SM-G9008W; userid=" + mUserId + "; "
                                        + "sessionid=" + mSessionId + "; "
                                        + "sign=" + genCreateRoomSign("777", urlParams) + "; ";
                        Log.d(TAG, "cookies = " + cookies);
                } catch (NoSuchAlgorithmException e) {
                        e.printStackTrace();
                        return ;
                }
                AsyncTask<String, Integer, String> task = null;
                if (isCreateRoom) { // Create Room
                        task = new CreateRoomTask(context);
                } else { // Close Room
                        task = new CloseRoomTask(context, logout);
                }
                task.executeOnExecutor(Executors.newCachedThreadPool(), url, cookies);
        }

        private void logout() {
                if (null == mUserId) {
                        return ;
                }
                try {
                        LogoutTask task = new LogoutTask();
                        String params = genLogoutUrlParam();
                        if (null == params) {
                                return ;
                        }
                        task.executeOnExecutor(Executors.newCachedThreadPool()
                                        , "https://api.penquanyun.com/auth/mobile/logout?" + params);
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }

        private String genNonceStr (int len) {
                if ((len <= 0) || (len > 4096)) {
                        return "";
                }
                Random rand = new Random();
                StringBuilder builder = new StringBuilder("");
                for (int i = 0; i < len; i++) {
                        int index = rand.nextInt(62);
                        builder.append(randCharAry[index]);
                }
                return builder.toString();
        }

        private String md5sum (String str) throws NoSuchAlgorithmException {
                StringBuilder result = new StringBuilder("");
                MessageDigest md = MessageDigest.getInstance("md5");
                md.update(str.getBytes());
                byte [] md5 = md.digest();
                for (int i = 0; i < md5.length; i++) {
                        int b = md5[i] & 0xFF;
                        if (b <= 0xF) {
                                result.append("0");
                        }
                        result.append(Integer.toHexString(b));
                }
                return result.toString();
        }

        private String genLoginUrlParam() throws NoSuchAlgorithmException {
                String uid =  SystemProperties.get(KEY_LIVEUID, "");
                if (TextUtils.isEmpty(uid)) {
                        return null;
                }
                Map<String, String> paramsMap = new TreeMap<String, String>(new ParamNameComparator());
                paramsMap.put("cooperator_id", "777");
                paramsMap.put("fcappid", "live_1");
                paramsMap.put("third_auth", "zhiborui");
                paramsMap.put("third_appid", "1");
                paramsMap.put("openid", uid);
                paramsMap.put("access_token", uid);
                paramsMap.put("terminal_type", "glass");
                paramsMap.put("nonce_str", genNonceStr(5));
                StringBuilder signBuilder = new StringBuilder("");
                StringBuilder paramBuilder = new StringBuilder("");
                Set<Map.Entry<String, String>> paramsEntry = paramsMap.entrySet();
                for (Iterator<Map.Entry<String, String>> it = paramsEntry.iterator(); it.hasNext(); ) {
                        Map.Entry<String, String> entry = it.next();
                        String param = entry.getKey() + "=" + entry.getValue() + "&";
                        signBuilder.append(param);
                        paramBuilder.append(param);
                }
                signBuilder.append("secret=X0waiPz4McQENuCs");
                String sign = md5sum(signBuilder.toString()).toLowerCase();
                paramBuilder.append("sign=").append(sign);
                return paramBuilder.toString();
        }

        private String genLogoutUrlParam() throws NoSuchAlgorithmException {
                if (null == mUserId) {
                        return null;
                }
                Map<String, String> paramsMap = new TreeMap<String, String>(new ParamNameComparator());
                paramsMap.put("cooperator_id", "777");
                paramsMap.put("fcappid", "live_1");
                paramsMap.put("userid", mUserId);
                paramsMap.put("sessionid", mSessionId);
                paramsMap.put("nonce_str", genNonceStr(5));
                StringBuilder signBuilder = new StringBuilder("");
                StringBuilder paramBuilder = new StringBuilder("");
                Set<Map.Entry<String, String>> paramsEntry = paramsMap.entrySet();
                for (Iterator<Map.Entry<String, String>> it = paramsEntry.iterator(); it.hasNext(); ) {
                        Map.Entry<String, String> entry = it.next();
                        String param = entry.getKey() + "=" + entry.getValue() + "&";
                        signBuilder.append(param);
                        paramBuilder.append(param);
                }
                signBuilder.append("secret=X0waiPz4McQENuCs");
                String sign = md5sum(signBuilder.toString()).toLowerCase();
                paramBuilder.append("sign=").append(sign);
                return paramBuilder.toString();
        }

        private String genCreateRoomSign(String appid, String params) throws NoSuchAlgorithmException {
                Map<String, String> paramsMap = new TreeMap<String, String>(new ParamNameComparator());
                String [] paramAry = params.split("&");
                for (int i = 0; i < paramAry.length; i++) {
                        String [] part = paramAry[i].split("=");
                        if (part.length > 1) {
                                paramsMap.put(part[0], part[1]);
                        }
                }
                StringBuilder signBuilder = new StringBuilder(appid);
                Set<Map.Entry<String, String>> paramsEntry = paramsMap.entrySet();
                for (Iterator<Map.Entry<String, String>> it = paramsEntry.iterator(); it.hasNext(); ) {
                        Map.Entry<String, String> entry = it.next();
                        signBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
                }
                if (paramsMap.size() > 0) {
                        signBuilder.deleteCharAt(signBuilder.length() - 1);
                }
                signBuilder.append("908TYTRUYIfdsf#$%&");
                return md5sum(signBuilder.toString()).toLowerCase();
        }

        private String sendHttpGet (String urlstr) throws Exception {
                return sendHttpGet(urlstr, null);
        }

        private String sendHttpGet (String urlstr, String cookies) throws Exception {
                return sendHttpRequest("GET", urlstr, cookies, null);
        }

        private String sendHttpPost (String urlstr, String cookies, String data) throws Exception {
                return sendHttpRequest("POST", urlstr, cookies, data);
        }

        private String sendHttpRequest (String requestMethod, String urlstr, String cookies, String data) throws Exception {
                HttpURLConnection conn = null;

                if (TextUtils.isEmpty(urlstr)) {
                        return null;
                }

                URL url = new URL(urlstr);
                conn = (HttpURLConnection) url.openConnection();
                return sendRequest(conn, requestMethod, cookies, data);
        }

        private String sendHttpsGet (String urlstr) throws Exception {
                return sendHttpsGet(urlstr, null);
        }

        private String sendHttpsGet (String urlstr, String cookies) throws Exception {
                return sendHttpsRequest("GET", urlstr, cookies, null);
        }

        private String sendHttpsPost (String urlstr, String cookies, String data) throws Exception {
                return sendHttpsRequest("POST", urlstr, cookies, data);
        }

        private String sendHttpsRequest (String requestMethod, String urlstr, String cookies, String data) throws Exception {
                HttpsURLConnection conn = null;

                if (TextUtils.isEmpty(urlstr)) {
                        return null;
                }

                URL url = new URL(urlstr);
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[] { new MyTrustManager() }, new SecureRandom());
                HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
                HttpsURLConnection.setDefaultHostnameVerifier(new MyHostnameVerifier());
                conn = (HttpsURLConnection) url.openConnection();
                return sendRequest(conn, requestMethod, cookies, data);
        }

        private String sendRequest (HttpURLConnection conn, String requestMethod, String cookies, String data)
                        throws Exception {
                OutputStream out = null;
                BufferedReader br = null;
                StringBuilder builder = new StringBuilder("");

                if (TextUtils.isEmpty(requestMethod)) {
                        return null;
                }

                try {
                        conn.setInstanceFollowRedirects(false);
                        conn.setRequestMethod(requestMethod);
                        if ("GET".equals(requestMethod.trim())) {
                                conn.setDoOutput(false); // The request use of "GET" must be set to false.
                        } else {
                                conn.setDoOutput(true);
                        }
                        conn.setDoInput(true);
                        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
                        conn.setRequestProperty("Connection", "Keep-Alive");
                        if ("POST".equals(requestMethod.trim()) && (!TextUtils.isEmpty(data))) {
                                conn.setRequestProperty("Content-Length", String.valueOf(data.getBytes().length));
                        }
                        if (!TextUtils.isEmpty(cookies)) {
                                conn.setRequestProperty("Cookie", cookies);
                        }
                        conn.connect();

                        if ("POST".equals(requestMethod.trim())) {
                                out = new DataOutputStream(conn.getOutputStream());
                                if (!TextUtils.isEmpty(data)) {
                                        out.write(data.getBytes());
                                }
                                out.flush();
                        }

                        br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                        String line = null;
                        while ((line = br.readLine()) != null) {
                                builder.append(line);
                        }
                } catch (Exception e) {
                        throw e;
                } finally {
                        try {
                                if (null != br) {
                                        br.close();
                                }
                                if (null != out) {
                                        out.flush();
                                        out.close();
                                }
                        } catch (IOException e) {
                                e.printStackTrace();
                        }
                        if (null != conn) {
                                conn.disconnect();
                        }
                }
                return builder.toString();
        }

        class LoginTask extends AsyncTask<String, Integer, String> {
                private Context mContext = null;
                private boolean mCreateRoom = false;
                LoginTask (Context context, boolean createRoom) {
                        mContext = context;
                        mCreateRoom = createRoom;
                }

                @Override
                protected String doInBackground(String... params) {
                        try {
                                Log.d(TAG, "url = " + params[0]);
                                String str = sendHttpsGet(params[0]);
                                Log.d(TAG, "ret = " + str);
                                JSONObject jsonObj = new JSONObject(str);
                                String ret = jsonObj.getString("ret");
                                if ("0".equals(ret)) {
                                        JSONObject jsonObjData = jsonObj.getJSONObject("data");
                                        mUserId = jsonObjData.getString("userid");
                                        mSessionId = jsonObjData.getString("sessionid");
                                }
                        } catch (Exception e) {
                                e.printStackTrace();
                                return TASKRET_FAILED;
                        }
                        return TASKRET_SUCCESS;
                }

                @Override
                protected void onPostExecute(String result) {
                        if (TASKRET_SUCCESS.equals(result) && mCreateRoom) {
                                room(mContext, true, false);
                        } else {
                                try {
                                        setCurrentLiveStatus("start_failed");
                                        SyncData data = new SyncData();
                                        data.putInt("type", PqyLiveModule.TYPE_LIVESTATUS);
                                        data.putString(PqyLiveModule.KEY_LIVESTATUS, getCurrentLiveStatus());
                                        PqyLiveModule.getInstance(mContext).send(data);
                                } catch (SyncException e) {
                                        e.printStackTrace();
                                }
                        }
                }
        }

        class CreateRoomTask extends AsyncTask<String, Integer, String> {
                private Context mContext = null;
                CreateRoomTask (Context context) {
                        mContext = context;
                }

                @Override
                protected String doInBackground(String... params) {
                        String url = params[0];
                        String cookies = params[1];
                        try {
                                String str = sendHttpPost(url, cookies, "title=zhiborui");
                                Log.d(TAG, "ret = " + str);
                                JSONObject jsonObj = new JSONObject(str);
                                String result = jsonObj.getString("result");
                                String message = jsonObj.getString("message");
                                Log.d(TAG, "result = " + result + ", message = " + message);
                                if ("0".equals(result)) {
                                        JSONObject jsonObjData = jsonObj.getJSONObject("data");
                                        String rtmpPushAddr = jsonObjData.getString("stream_push");
                                        String rtmpPullAddr = jsonObjData.getString("stream_pull");
                                        mRoomId = jsonObjData.getString("roomid");
                                        Log.d(TAG, "roomid = " + mRoomId);
                                        Log.d(TAG, "stream_push = " + rtmpPushAddr);
                                        Log.d(TAG, "stream_pull = " + rtmpPullAddr);

                                        Intent intent = new Intent();
                                        intent.setAction("com.ingenic.glass.camera.CameraRTMPLive");
                                        intent.putExtra("liveUrl", rtmpPushAddr);
                                        mContext.sendBroadcast(intent);
                                        return TASKRET_SUCCESS;
                                }
                        } catch (Exception e) {
                                e.printStackTrace();
                                return TASKRET_FAILED;
                        }
                        return TASKRET_FAILED;
                }

                @Override
                protected void onPostExecute(String result) {
                        if (!TASKRET_SUCCESS.equals(result)) {
                                logout();
                                try {
                                        setCurrentLiveStatus("create_failed");
                                        SyncData data = new SyncData();
                                        data.putInt("type", PqyLiveModule.TYPE_LIVESTATUS);
                                        data.putString(PqyLiveModule.KEY_LIVESTATUS, getCurrentLiveStatus());
                                        PqyLiveModule.getInstance(mContext).send(data);
                                } catch (SyncException e) {
                                        e.printStackTrace();
                                }
                        }
                }
        }

        class CloseRoomTask extends AsyncTask<String, Integer, String> {
                private Context mContext = null;
                private boolean mLogout = false;
                CloseRoomTask (Context context, boolean logout) {
                        mContext = context;
                        mLogout = logout;
                }

                @Override
                protected String doInBackground(String... params) {
                        if ((null == mUserId) || (null == mRoomId)) {
                                return null;
                        }
                        try {
                                String url = params[0];
                                String cookies = params[1];
                                String str = sendHttpGet(url, cookies);
                                Log.d(TAG, "ret = " + str);
                        } catch (Exception e) {
                                e.printStackTrace();
                        }
                        return null;
                }

                @Override
                protected void onPostExecute(String result) {
                        if (mLogout) {
                                logout();
                        }
                }
        }

        class LogoutTask extends AsyncTask<String, Integer, String> {

                @Override
                protected String doInBackground(String... params) {
                        if (null == mUserId) {
                                return null;
                        }
                        mUserId = null;
                        mSessionId = null;
                        try {
                                Log.d(TAG, "url = " + params[0]);
                                String str = sendHttpsGet(params[0]);
                                Log.d(TAG, "ret = " + str);
                        } catch (Exception e) {
                                e.printStackTrace();
                        }
                        return null;
                }
        }

        class MyTrustManager implements X509TrustManager {

                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                                String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                                String authType) throws CertificateException {
                        
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                        return null;
                }
        }

        class MyHostnameVerifier implements HostnameVerifier {

                @Override
                public boolean verify(String hostname, SSLSession session) {
                        Log.d(TAG, "verify hostname = " + hostname);
                        return true;
                }
        }

        class ParamNameComparator implements Comparator<String> {

                @Override
                public int compare(String o1, String o2) {
                        return o1.compareTo(o2);
                }
        }

        public static String getCurrentLiveStatus() {
                return mCurrentLiveStatus;
        }

        public static void setCurrentLiveStatus(String mCurrentLiveStatus) {
                PqyLiveBroadcastReceiver.mCurrentLiveStatus = mCurrentLiveStatus;
        }
}